#include "lcd.h"

// https://developer.arm.com/documentation/dui0159/b/peripherals-and-interfaces/display-interface/color-lcd-registers
// https://developer.arm.com/documentation/ddi0161/e/programmer-s-model/summary-of-registers

struct lcd_regs
{
    unsigned LCDTiming0;
    unsigned LCDTiming1;
    unsigned LCDTiming2;
    unsigned LCDTiming3;
    unsigned LCDUPBASE;
    unsigned LCDLPBASE;
    unsigned LCDIMSC;
    unsigned LCDControl;
};

#define LCD (*(volatile struct lcd_regs *)0xc0000000)

#define LCD_WIDTH 320
#define LCD_HEIGHT 240

static volatile unsigned frame_buffer[LCD_HEIGHT][LCD_WIDTH];

void init_lcd(void)
{
    // HBP = 38 - 1 (horizontal back porch = 38)
    // HFP = 20 - 1 (horizontal front porch = 20)
    // HSW = 30 - 1 (horizontal synchronization pulse width = 30)
    // PPL = LCD_WIDTH / 16 - 1 (pixels-per-line = LCD_WIDTH)
    LCD.LCDTiming0 = ((38 - 1) << 24) | ((20 - 1) << 16) | ((30 - 1) << 8) | ((LCD_WIDTH / 16 - 1) << 2);

    // VBP = 15 - 1 (vertical back porch = 15)
    // VFP = 12 - 1 (vertical front porch = 12)
    // VSW = 3 - 1 (vertical synchronization pulse width = 3)
    // LPP = 240 - 1 (lines per panel = LCD_HEIGHT)
    LCD.LCDTiming1 = ((15 - 1) << 24) | ((12 - 1) << 16) | ((3 - 1) << 10) | (LCD_HEIGHT - 1);

    // BCD = 1 (bypass pixel clock divider)
    // CPL = LCD_WIDTH - 1 (clocks per line = pixels-per-line)
    LCD.LCDTiming2 = (1 << 26) | ((LCD_WIDTH - 1) << 16);

    // LCD line end disabled
    LCD.LCDTiming3 = 0;

    LCD.LCDUPBASE = (unsigned)frame_buffer;

    // LcdPwr = 1 (LCD power enable)
    // BGR = 1 (0x00rrggbb ?)
    // LcdTFT = 1 (LCD is TFT)
    // LcdBpp = 101 (24 bpp)
    // LcdEn = 1 (LCD controller enable)
    LCD.LCDControl = 0b100100101011;
}

void lcd_draw_test(void)
{
    for (int i = 0; i < LCD_HEIGHT; i++)
    {
        for (int j = 0; j < LCD_WIDTH; j++)
        {
            if (i < LCD_HEIGHT / 2)
            {
                switch (j / (LCD_WIDTH / 4))
                {
                case 0:
                    frame_buffer[i][j] = 0xff0000;
                    break;
                case 1:
                    frame_buffer[i][j] = 0x00ff00;
                    break;
                case 2:
                    frame_buffer[i][j] = 0xffff00;
                    break;
                case 3:
                    frame_buffer[i][j] = 0x0000ff;
                    break;
                }
            }
            else frame_buffer[i][j] = 0xcccccc;
        }
    }
}
